package com.example.demofx.master;

import javafx.scene.image.Image;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class OptimizedPhotoLoader implements PhotoLoader {

    private final Map<String, Image> photoCache = new HashMap<>();
    private final Map<String, File> compressedPhotoCache = new HashMap<>();
    private final ExecutorService executorService = Executors.newFixedThreadPool(2);
    private final String compressedPhotoDir;

    public OptimizedPhotoLoader(String cacheDir) {
        this.compressedPhotoDir = cacheDir + "/compressed";
        File dir = new File(compressedPhotoDir);
        if (!dir.exists()) {
            dir.mkdirs();
        }
    }

    @Override
    public Optional<Image> loadPhoto(File photoFile, int targetWidth, int targetHeight) {
        if (photoFile == null || !photoFile.exists()) {
            return Optional.empty();
        }

        try {
            String filePath = photoFile.getAbsolutePath();

            // 检查缓存
            if (photoCache.containsKey(filePath)) {
                return Optional.of(photoCache.get(filePath));
            }

            // 检查是否需要压缩
            File compressedFile = getOrCompressPhoto(photoFile, targetWidth, targetHeight, 0.7);

            // 加载图片
            Image image = new Image(new FileInputStream(compressedFile),
                    targetWidth, targetHeight, true, true);

            // 添加到缓存
            photoCache.put(filePath, image);

            return Optional.of(image);

        } catch (IOException e) {
            System.err.println("Error loading photo: " + e.getMessage());
            e.printStackTrace();
            return Optional.empty();
        }
    }

    @Override
    public CompletableFuture<Optional<Image>> loadPhotoAsync(File photoFile, int targetWidth, int targetHeight) {
        return CompletableFuture.supplyAsync(() -> loadPhoto(photoFile, targetWidth, targetHeight), executorService);
    }

    @Override
    public Optional<File> compressPhoto(File originalFile, int targetWidth, int targetHeight, double quality) {
        if (originalFile == null || !originalFile.exists()) {
            return Optional.empty();
        }

        try {
            // 创建压缩后的文件名
            String fileName = originalFile.getName();
            String fileExtension = fileName.substring(fileName.lastIndexOf("."));
            String compressedFileName = fileName.substring(0, fileName.lastIndexOf(".")) +
                    "_compressed_" + targetWidth + "x" + targetHeight + fileExtension;
            File compressedFile = new File(compressedPhotoDir, compressedFileName);

            // 如果已存在压缩文件，直接返回
            if (compressedFile.exists()) {
                return Optional.of(compressedFile);
            }

            // 读取原始图片
            BufferedImage originalImage = ImageIO.read(originalFile);

            // 计算新尺寸，保持宽高比
            double ratio = Math.min((double)targetWidth / originalImage.getWidth(),
                    (double)targetHeight / originalImage.getHeight());
            int newWidth = (int) (originalImage.getWidth() * ratio);
            int newHeight = (int) (originalImage.getHeight() * ratio);

            // 调整大小
            BufferedImage resizedImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = resizedImage.createGraphics();
            g2d.drawImage(originalImage, 0, 0, newWidth, newHeight, null);
            g2d.dispose();

            // 保存压缩后的图片
            if (fileExtension.toLowerCase().endsWith("jpg") || fileExtension.toLowerCase().endsWith("jpeg")) {
                // JPEG格式压缩
                Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("JPEG");
                if (!writers.hasNext()) {
                    throw new IllegalStateException("No JPEG writer found");
                }

                ImageWriter writer = writers.next();
                ImageWriteParam param = writer.getDefaultWriteParam();
                param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                param.setCompressionQuality((float) quality);

                try (ImageOutputStream ios = ImageIO.createImageOutputStream(compressedFile)) {
                    writer.setOutput(ios);
                    writer.write(null, new IIOImage(resizedImage, null, null), param);
                }
            } else {
                // 其他格式使用默认方式
                ImageIO.write(resizedImage, fileExtension.substring(1), compressedFile);
            }

            // 添加到压缩缓存
            compressedPhotoCache.put(originalFile.getAbsolutePath(), compressedFile);

            return Optional.of(compressedFile);

        } catch (IOException e) {
            System.err.println("Error compressing photo: " + e.getMessage());
            e.printStackTrace();
            return Optional.empty();
        }
    }

    private File getOrCompressPhoto(File originalFile, int targetWidth, int targetHeight, double quality) {
        String filePath = originalFile.getAbsolutePath();

        // 检查是否已有压缩版本
        if (compressedPhotoCache.containsKey(filePath)) {
            File cachedCompressedFile = compressedPhotoCache.get(filePath);
            if (cachedCompressedFile.exists()) {
                return cachedCompressedFile;
            }
        }

        // 没有缓存或缓存已失效，执行压缩
        Optional<File> compressedFile = compressPhoto(originalFile, targetWidth, targetHeight, quality);
        return compressedFile.orElse(originalFile);
    }

    @Override
    public void clearCache() {
        photoCache.clear();
        compressedPhotoCache.clear();
    }

    public void shutdown() {
        executorService.shutdown();
    }
}